#!/usr/bin/env python
# encoding: utf-8

"""
@version: python3.7
@Author  : huangyi
@contact:  hy.dazui@qq.com
@Time    : 2020/1/13 1:03
@Describe: add Describe here
"""
from ..expression.nonterminal.in_expression import InExpression
from ..expression.nonterminal.and_expression import AndExpression
from ..expression.nonterminal.div_expression import DivExpression
from ..expression.nonterminal.eq_expression import EqExpression
from ..expression.nonterminal.func_expression import FuncExpression
from ..expression.nonterminal.ge_expression import GeExpression
from ..expression.nonterminal.gt_expression import GtExpression
from ..expression.nonterminal.le_expression import LeExpression
from ..expression.nonterminal.lt_expression import LtExpression
from ..expression.nonterminal.multi_expression import MultiExpression
from ..expression.nonterminal.or_expression import OrExpression
from ..expression.nonterminal.plus_expression import PlusExpression
from ..expression.nonterminal.sub_expression import SubExpression
from ..expression.terminal.float_expression import FloatExpression
from ..expression.terminal.int_expression import IntExpression
from ..expression.terminal.string_expression import StringExpression
from ..expression.terminal.var_expression import VarExpression
from ..scanner.token.token import TokenType


class AstExpressionStack:

    def __init__(self):
        self.contain_stack = [[]]
        self.current_stack_index = 0

    def append(self, data):
        self.contain_stack[self.current_stack_index].append(data)

    def pop(self):
        return self.contain_stack[self.current_stack_index].pop()

    def is_empty(self):
        return len(self.contain_stack[self.current_stack_index]) == 0

    def enter_func(self):
        self.contain_stack.append([])
        self.current_stack_index += 1

    def leave_func(self):
        if len(self.contain_stack) > 1:
            # 把将要离开的方法区栈内内容返回
            tmp_stack_data = self.contain_stack[self.current_stack_index]
            self.contain_stack.pop()
            self.current_stack_index -= 1
            return tmp_stack_data
        return []

    def is_func_stack(self):
        return self.current_stack_index > 0


class Ast:
    """
    构建语法树
    """

    def __init__(self):
        # 解释器栈
        self.expression_stack = AstExpressionStack()
        # 符号栈（包含运算符和括号）
        self.op_stack = []

        self.op_level = {
            "(": 1,
            ")": 100,
            "func": 2,
            "*": 3,
            "/": 3,
            "+": 4,
            "-": 4,
            ">=": 5,
            "<=": 5,
            ">": 5,
            "<": 5,
            "==": 5,
            "in": 5,
            "and": 6,
            "or": 6,
            "&&": 6,
            "||": 6,
            ",": 7
        }

    def parse(self, token_list: list):
        if token_list:
            for token in token_list:
                if token.get_type() in [TokenType.NUMBER, TokenType.POINT, TokenType.STRING, TokenType.VAR]:
                    expression = self.build_base_type_expression(token)
                    self.expression_stack.append(expression)
                elif token.get_type() == TokenType.FUNC:
                    self.op_stack.append(token)
                    self.expression_stack.enter_func()
                else:
                    # 当前符号的优先级高于栈顶，或栈顶为空，则入栈
                    if not self.op_stack:
                        if token.get_type() == TokenType.RIGHT_BRACKET:
                            continue
                        self.op_stack.append(token)
                    else:
                        if self.check_can_append(token):
                            # 优先级比栈顶高的直接入栈
                            self.op_stack.append(token)
                        else:
                            if token.get_type() != TokenType.RIGHT_BRACKET:
                                #  当前符号token入栈
                                if not self.check_can_append(token):
                                    self.build_tree_node(token)
                                self.op_stack.append(token)
                            else:
                                self.build_tree_node(token)

            # 最后只剩下一个解释器，就是语法树的根
            return self.expression_stack.pop()
        return None

    def build_tree_node(self, token):
        if self.expression_stack.is_func_stack() and self.expression_stack.is_empty():
            # 无参数方法
            # 弹出左括号
            self.op_stack.pop()
            # 弹出方法名
            pop_token = self.op_stack.pop()
            # 当前是方法栈时，则离开方法栈
            func_stack_data_list = self.expression_stack.leave_func()
            func_expression = self.build_func_expression(pop_token, *func_stack_data_list)
            self.expression_stack.append(func_expression)
        else:
            while self.op_stack and not self.check_can_append(token) and not self.expression_stack.is_empty():
                pop_token = self.op_stack.pop()
                if pop_token.get_type() == TokenType.LEFT_BRACKET:

                    if self.expression_stack.is_func_stack():
                        # 有参数方法
                        # 弹出方法名
                        pop_token = self.op_stack.pop()
                        # 当前是方法栈时，则离开方法栈
                        func_stack_data_list = self.expression_stack.leave_func()
                        func_expression = self.build_func_expression(pop_token, *func_stack_data_list)
                        self.expression_stack.append(func_expression)
                    else:
                        # 左括号直接弹出栈，不计算
                        break
                elif pop_token.get_type() == TokenType.COMMA:
                    continue
                else:
                    expression1 = self.expression_stack.pop()
                    expression2 = self.expression_stack.pop()
                    # 栈的弹出顺序和表达式顺序相反
                    expression = self.build_op_expression(pop_token, expression2, expression1)
                    # 入解释器栈
                    self.expression_stack.append(expression)

    def check_can_append(self, token):
        top_token = self.op_stack[-1]
        if top_token.get_type() == TokenType.LEFT_BRACKET and token.get_type() != TokenType.RIGHT_BRACKET:
            # 栈顶是左括号时，除非遇到右括号，否则一律允许压入栈
            return True
        top_token_lv = self.get_token_lv(top_token)
        current_token_lv = self.get_token_lv(token)
        return current_token_lv < top_token_lv

    def get_token_lv(self, token):
        token_lv = 9999
        if token.get_type() == TokenType.FUNC:
            token_lv = self.op_level.get("func", 9999)
        else:
            token_lv = self.op_level.get(token.get_data(), 9999)
        return token_lv

    def build_base_type_expression(self, token):
        expression = None
        if token.get_type() == TokenType.POINT:
            expression = FloatExpression(token.get_data())
        elif token.get_type() == TokenType.NUMBER:
            expression = IntExpression(token.get_data())
        elif token.get_type() == TokenType.STRING:
            expression = StringExpression(token.get_data())
        elif token.get_type() == TokenType.VAR:
            expression = VarExpression(token.get_data())
        else:
            raise Exception("unsupported token type: {}".format(token.get_type()))
        return expression

    def build_op_expression(self, token, expression1, expression2):
        expression = None
        if token.get_data() == "+":
            expression = PlusExpression(expression1, expression2)
        elif token.get_data() == "-":
            expression = SubExpression(expression1, expression2)
        elif token.get_data() == "*":
            expression = MultiExpression(expression1, expression2)
        elif token.get_data() == "/":
            expression = DivExpression(expression1, expression2)
        elif token.get_data() in ["&&", "and"]:
            expression = AndExpression(expression1, expression2)
        elif token.get_data() in ["||", "or"]:
            expression = OrExpression(expression1, expression2)
        elif token.get_data() == "==":
            expression = EqExpression(expression1, expression2)
        elif token.get_data() == "<":
            expression = LtExpression(expression1, expression2)
        elif token.get_data() == "<=":
            expression = LeExpression(expression1, expression2)
        elif token.get_data() == ">":
            expression = GtExpression(expression1, expression2)
        elif token.get_data() == ">=":
            expression = GeExpression(expression1, expression2)
        elif token.get_data() == "in":
            expression = InExpression(expression1, expression2)
        else:
            raise Exception("unsupported op: {}".format(token.get_data()))
        return expression

    def build_func_expression(self, pop_token, *param):
        return FuncExpression(pop_token.get_data(), *param)
